package edu.osu.cse.c651.cryptochess.db;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import edu.osu.cse.c651.cryptochess.User;
import edu.osu.cse.c651.cryptochess.UserIcon;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Base64;

public class UserDatabaseHelper extends SQLiteOpenHelper {

	final private static String dbName = "cryptoUsers";
	final private static int version = 3;
	
	final private static String SQL_TABLE_CREATE = "CREATE TABLE "
			+ dbName + " ( username TEXT PRIMARY KEY, wins INTEGER, "
			+ "losses INTEGER, draws INTEGER, rating INTEGER, "
			+ "icon INTEGER, pwd TEXT, salt TEXT);";
	final private static String SQL_QUERY_GET_USERS = "SELECT username,"
			+ "wins,losses,draws,rating,icon FROM " + dbName + ";";
	final private static String SQL_QUERY_GET_USERNAMES = "SELECT username "
			+ "FROM " + dbName + ";";
	
	public UserDatabaseHelper(Context context) {
		super(context, dbName, null, version);	
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL(SQL_TABLE_CREATE);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		db.execSQL("DROP TABLE " + dbName + ";");
		db.execSQL(SQL_TABLE_CREATE);
	}
	
	public User[] getUsers() {
		SQLiteDatabase db = this.getReadableDatabase();
		Cursor c = db.rawQuery(SQL_QUERY_GET_USERS, new String[] {});
		
		if(c != null) {
			int count = c.getCount();
			User[] u = new User[count];
			c.moveToFirst();
			
			for(int i = 0; i < count; i++) {
				String username = c.getString(0);
				int win = c.getInt(1);
				int loss = c.getInt(2);
				int draw = c.getInt(3);
				int rating = c.getInt(4);
				int icon = c.getInt(5);
				
				u[i] = new User(username, win, loss, draw, rating, UserIcon.values()[icon]);
				
				c.moveToNext();
			}
			db.close();
			return u;
		}
		db.close();
		return null;
	}
	
	public void updateUserRecord(User u) {
		SQLiteDatabase db = this.getWritableDatabase();
		ContentValues args = new ContentValues();
	    args.put("rating", u.getRating());
	    args.put("wins", u.getWins());
	    args.put("losses", u.getLosses());
	    args.put("draws", u.getDraws());
	    db.update(dbName, args, "username = '" + u.getUsername() + "'", null);
	}
	
	public boolean checkPassword(String username, String password) {
		SQLiteDatabase db = this.getReadableDatabase();
		Cursor c = db.rawQuery("SELECT pwd,salt FROM " + dbName + " WHERE username = '" + username + "';", new String[] {});
		if(c != null) {
			int count = c.getCount();
			c.moveToFirst();
			if(count > 0) {
				String hashedPwdString = c.getString(0);
				String saltString = c.getString(1);
				
				// Hash the password that was passed in to see if it matches
				try {
					MessageDigest md = MessageDigest.getInstance("MD5");

					byte[] passBytes = password.getBytes("UTF-8");
					byte[] salt = Base64.decode(saltString, Base64.DEFAULT);
					
					// Copy the arrays into one byte array to hash
					byte[] saltPass = new byte[salt.length + passBytes.length];
					for(int i = 0; i < salt.length; i++) {
						saltPass[i] = salt[i];
					}
					for(int i = 0; i < passBytes.length; i++) {
						saltPass[salt.length + i] = passBytes[i];
					}
					
					md.update(saltPass);
					byte[] hashBytes = md.digest();
					String hashString = Base64.encodeToString(hashBytes, Base64.DEFAULT);
					
					if(hashString.equals(hashedPwdString)) {
						db.close();
						return true;
					}
					else {
						db.close();
						return false;
					}
				} catch (NoSuchAlgorithmException e) {
					e.printStackTrace();
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
			}
		}
		db.close();
		return false;
	}
	
	public String addUser(User u) {
		boolean usernameExists = false;
		
		SQLiteDatabase db = this.getWritableDatabase();
		Cursor c = db.rawQuery(SQL_QUERY_GET_USERNAMES, new String[] {});
		
		if(c != null && c.moveToFirst()) {
			do {
				String un = c.getString(0);
				if(u.getUsername().equals(un)) {
					usernameExists = true;
					break;
				}
			} while(c.moveToNext());
		}
		
		if(usernameExists) {
			return "Username is already used for another local account.";
		}
		
		ContentValues  cv = new ContentValues ();
		cv.put("username", u.getUsername());
		cv.put("wins", u.getWins());
		cv.put("losses", u.getLosses());
		cv.put("draws", u.getDraws());
		cv.put("rating", u.getRating());
		cv.put("icon", u.getUserIcon().ordinal());
		
		// Salt and hash password using MD5
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
			
			byte[] salt = new byte[40];
			sr.nextBytes(salt);
			
			String pass = u.getPassword();
			byte[] passBytes = pass.getBytes("UTF-8");
			
			// Copy the arrays into one byte array to hash
			byte[] saltPass = new byte[salt.length + passBytes.length];
			for(int i = 0; i < salt.length; i++) {
				saltPass[i] = salt[i];
			}
			for(int i = 0; i < passBytes.length; i++) {
				saltPass[salt.length + i] = passBytes[i];
			}
			
			md.update(saltPass);
			byte[] hashBytes = md.digest();
			String hashString = Base64.encodeToString(hashBytes, Base64.DEFAULT);
			String saltString = Base64.encodeToString(salt, Base64.DEFAULT);
			
			cv.put("pwd", hashString);
			cv.put("salt", saltString);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		db.insert(dbName, null, cv);
		return null;
	}
}
